/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.gmi.controller;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Laurent WOUTERS
 */
public abstract class RepeatElement extends Element implements xowl.gmi.view.diagram.RepetitionWidget.Actions {
    protected xowl.gmi.view.diagram.RepetitionWidget widget;
    protected List<Element> children;
    protected boolean isOrdered;
    
    public RepeatElement(Controller controller, Element parent, xowl.gmi.model.cvssl.Alignment align, boolean isPacked, boolean isOrdered) {
        super(controller, parent);
        this.children = new ArrayList<>();
        this.isOrdered = isOrdered;
        widget = new xowl.gmi.view.diagram.RepetitionWidget(this, getAlignment(align), isPacked, isOrdered);
    }
    
    public void addChild(Element child) {
        this.children.add(child);
        this.widget.addChild(child.getWidget());
    }
    
    @Override public xowl.gmi.view.diagram.Widget getWidget() { return widget; }
    
    @Override protected int getContainedASTCount() {
        int count = 0;
        for (Element child : children)
            count += child.getContainedASTCount();
        return count;
    }
    @Override protected int getLeftSiblingsASTCount(Element divider) {
        int count = 0;
        for (Element child : children) {
            if (child == divider)
                break;
            count += child.getContainedASTCount();
        }
        return count + parent.getLeftSiblingsASTCount(this);
    }
    @Override protected boolean insertAST(xowl.gmi.model.ast.ASTNode node) {
        Element child = ASTMatcher.buildControlTree(controller, this, getChildElement(), node, false);
        if (child == null)
            return false;
        if (isOrdered) {
            addOnHold(node, child);
            return true;
        }
        children.add(child);
        widget.addChild(child.getWidget());
        controller.onDiagramUpdated();
        return true;
    }
    @Override protected ASTRemovalStatus removeAST(xowl.gmi.model.ast.ASTNode node) {
        for (Element child : children) {
            ASTRemovalStatus value = child.removeAST(node);
            if (value == ASTRemovalStatus.Done)
                return value;
            if (value == ASTRemovalStatus.NodeFound) {
                child.decommission();
                widget.removeChild(child.getWidget());
                children.remove(child);
                controller.onDiagramUpdated();
                return ASTRemovalStatus.Done;
            }
        }
        return ASTRemovalStatus.NodeNotFound;
    }
    
    @Override public void saveTo(xowl.gmi.model.Diagram diagram) {
        for (Element child : children)
            child.saveTo(diagram);
    }
    @Override public void decommission() {
        for (Element child : children)
            child.decommission();
    }
    
    @Override public boolean acceptFlavor(java.awt.datatransfer.DataFlavor flavor) {
        return (flavor == Controller.flavorVariable || flavor == Controller.flavorASTNode);
    }
    @Override public boolean acceptData(java.awt.datatransfer.DataFlavor flavor, Object data) {
        if (flavor == Controller.flavorVariable) {
            xowl.gmi.model.cvssl.Variable var = (xowl.gmi.model.cvssl.Variable)data;
            return (ASTMatcher.matches(getChildElement(), var));
        } else if (flavor == Controller.flavorASTNode) {
            xowl.gmi.model.ast.ASTNode node = (xowl.gmi.model.ast.ASTNode)data;
            xowl.gmi.model.cvssl.Variable var = (xowl.gmi.model.cvssl.Variable)node.getSymbol();
            return (ASTMatcher.matches(getChildElement(), var));
        }
        return false;
    }
    @Override public boolean drop(java.awt.datatransfer.DataFlavor flavor, Object data, int index) {
        if (flavor == Controller.flavorVariable) {
            xowl.gmi.model.cvssl.Variable var = (xowl.gmi.model.cvssl.Variable)data;
            if (isOrdered) actionAddVariable(var, index);
            else actionAddVariable(var);
            return true;
        } else if (flavor == Controller.flavorASTNode) {
            xowl.gmi.model.ast.ASTNode node = (xowl.gmi.model.ast.ASTNode)data;
            if (isOrdered) actionAddNode(node, index);
            else actionAddNode(node);
            return true;
        }
        return false;
    }
    
    protected int getIndexOf(xowl.gmi.model.ast.ASTNode node) {
        xowl.gmi.model.ast.ASTNode pn = getASTParent().getASTNode();
        for (xowl.gmi.model.ast.Decorator decorator : node.getAllDecorators()) {
            if (decorator.getParent() == pn)
                return decorator.getIndex();
        }
        return 0;
    }
    protected void addOnHold(xowl.gmi.model.ast.ASTNode node, final Element child) {
        xowl.gmi.model.ast.ASTNode pn = getASTParent().getASTNode();
        for (final xowl.gmi.model.ast.Decorator decorator : node.getAllDecorators()) {
            if (decorator.getParent() == pn) {
                final xowl.interpreter.adapter.DataPropertyObserver observer = new xowl.interpreter.adapter.DataPropertyObserver() {
                    @Override public void onAdd(Object elem) {
                        int index = (Integer)elem;
                        index -= parent.getLeftSiblingsASTCount(RepeatElement.this);
                        children.add(index, child);
                        widget.addChild(child.getWidget(), index);
                        decorator.removeAllObservers();
                        controller.onDiagramUpdated();
                    }
                    @Override public void onRemove(Object elem) { }
                };
                decorator.addPropertyObserver("index", observer);
            }
        }
    }
    
    protected void actionAddVariable(xowl.gmi.model.cvssl.Variable var, int index) {
        index += parent.getLeftSiblingsASTCount(this);
        xowl.lang.owl2.IRI childNode = controller.getRepository().getIRI(controller.getNewNodeIRI());
        xowl.lang.owl2.IRI childSymbol = var.getIRI();
        xowl.lang.owl2.IRI parentNode = getNearestAST().getASTNode().getIRI();
        DiagramUIAction action = new DiagramUIAction(controller.getLocale().diagInsertElem);
        action.addInner(controller.getCommandBuilder().create(childNode, childSymbol));
        for (xowl.gmi.model.ast.ASTNode cn : getNearestAST().getASTNode().getAllChildren()) {
            int i = getIndexOf(cn);
            if (i >= index) {
                action.addInner(controller.getCommandBuilder().unorder(cn.getSymbol().getIRI(), parentNode, cn.getIRI(), i));
                action.addInner(controller.getCommandBuilder().order(cn.getSymbol().getIRI(), parentNode, cn.getIRI(), i+1));
            }
        }
        action.addInner(controller.getCommandBuilder().attach(childSymbol, parentNode, childNode));
        action.addInner(controller.getCommandBuilder().order(childSymbol, parentNode, childNode, index));
        controller.executeUIAction(action);
    }
    protected void actionAddNode(xowl.gmi.model.ast.ASTNode node, int index) {
        index += parent.getLeftSiblingsASTCount(this);
        DiagramUIAction action = new DiagramUIAction(controller.getLocale().diagInsertElem);
        xowl.lang.owl2.IRI childSymbol = node.getSymbol().getIRI();
        xowl.lang.owl2.IRI parentNode = getNearestAST().getASTNode().getIRI();
        xowl.lang.owl2.IRI childNode = node.getIRI();
        for (xowl.gmi.model.ast.ASTNode cn : getNearestAST().getASTNode().getAllChildren()) {
            int i = getIndexOf(cn);
            if (i >= index) {
                action.addInner(controller.getCommandBuilder().unorder(cn.getSymbol().getIRI(), parentNode, cn.getIRI(), i));
                action.addInner(controller.getCommandBuilder().order(cn.getSymbol().getIRI(), parentNode, cn.getIRI(), i+1));
            }
        }
        action.addInner(controller.getCommandBuilder().attach(childSymbol, parentNode, childNode));
        action.addInner(controller.getCommandBuilder().order(childSymbol, parentNode, childNode, index));
        controller.executeUIAction(action);
    }
    
    protected abstract xowl.gmi.model.cvssl.Element getChildElement();
    protected abstract boolean canRemoveAChild();
}
